/* -*-C-*-
 ##############################################################################
 #
 # File:        trice/src/modgroup.c
 # RCS:         "@(#)$Revision: 1.18 $ $Date: 94/03/09 11:11:31 $"
 # Description: routines for manipulation of module group data structures
 # Author:      Doug Passey
 # Created:     
 # Language:    C
 # Package:     E1430
 # Status:      "@(#)$State: Exp $"
 #
 # (C) Copyright 1992, Hewlett-Packard Company, all rights reserved.
 #
 ##############################################################################
 #
 # This files contains all of the routines to handle creating, deleting and
 # other manipulations of module groups.  A module group is one or more E1430
 # modules grouped together under a modGroupID such that they can be setup
 # easily with only one function call for the whole group.  Most functions in
 # the E1430 library will have as their first parameter a modGroupID to 
 # reference a particular grouping of modules.  The modGroupID is an
 # unsigned short returned by create_module_group(), and is just the index
 # (plus one) into an array of pointers to a modGroup.  A modGroup is a
 # dynamically allocated array of indices into the modStates array.  A modGroup
 # is created during a call to create_module_group().  The last element in the
 # array is set to -1 to indicate the end of the array.  The modStates array 
 # is an array of aModuleImage data structures for all of the E1430 modules 
 # in the system.  The aModuleImage data structure is defined in trice.h and
 # contains all of the current register information for a single E1430 module.
 #
 # That was the top down view .... let's look at it from the bottom up just
 # to make it clear:
 #
 # The modStates array is an array of state data structures, aModuleImage;
 # one for each E1430 module in the system.  This array is dynamically
 # allocated during the first call to create_module_group().  It is dynamically
 # expanded, if necessary, during sucessive calls to create_module_group().
 # The size of the array is kept in the variable modStatesSize.  This array
 # is not changed by a call to delete_module_group(), but is destroyed by
 # a call to to the cleanup routine, free_module_memory().
 #
 # Above the modStates array is one or more modGroups.  They are the user
 # defined module groupings created one at a time by each user call to 
 # create_module_group().  The modGroup is a dynamically allocated array 
 # of "pointers" into the modState array ... these "pointers" are to 
 # individual modules and are just the modState array index for that module.
 # A modGroup array is terminated with a -1.  A modGroup is destroyed by a
 # call to delete_module_group().  All modGroups are destroyed by a call to
 # free_module_memory().
 #
 # At the top of this heirarchy is the modGroupID array.  Each user call to
 # create_module_group() adds one element to this array.  This element is a
 # pointer to the modGroup array created in the call.  The value returned
 # by the call to create_module_group() is just the index of this element
 # plus one.  The variable, modGroupIdSize, contains the size of this array.
 # This array is dynamically expanded, if necessary, during a call to
 # create_module_group().  All unused entries in this array point to NULL.
 # A call to delete_module_group() will result in a NULL being written into
 # the array position holding the pointer to that module group.  A call to
 # free_module_memory() frees the whole array.
 #
 # Revisions:
 #
 ##############################################################################
*/


#    include <stdio.h>

#include "trice.h"
#include "err1430.h"

#ifndef lint
const char i1430_mod_fileId[] = "$Header: modgroup.c,v 1.18 94/03/09 11:11:31 chriss Exp $";
#endif

#define GROUPID_OFFSET	256

/******************************************************************************
 * Tests to see if there is a TRICE module at this logical address
 * Returns 1 or 0;
 *****************************************************************************/
static SHORTSIZ16 i1430_trice_at_la(SHORTSIZ16 la)
{
  SHORTSIZ16 deviceTypeReg;

  (void) e1430_read_register_card(la, E1430_VXI_DEVICE_TYPE_REG, &deviceTypeReg);
  if((deviceTypeReg & VXI_DEVICE_TYPE_MASK) == TRICE_DEVICE_TYPE) { 
    return (1);
  }else{
    return (0);
  }
}

/******************************************************************************
 * Tests to see if groupID is a valid group id.  Returns a pointer to the
 * group if ok, otherwise * it returns NULL.
 *****************************************************************************/
aModGroupPtr i1430_valid_module_group(SHORTSIZ16 groupID)
{
  char buf[80];

  /* check if a good group number in range GROUPID_OFFSET to
   * GROUPID _OFFSET + number of groups assigned - 1
   */
  if((groupID >= GROUPID_OFFSET) && 
			(groupID - GROUPID_OFFSET < e1430_modGroupIdSize)) {
    return (e1430_modGroupID[groupID - GROUPID_OFFSET]);

  /* check if in the logical address range of 1 to 255 */
  }else if(groupID > 0 && groupID < 256) {
    (void)sprintf(buf, "%d", (LONGSIZ32)groupID);
    (void)i1430_Error(ERR1430_GROUP_NOT_LA, buf, NULL);
    return (NULL);

  /* must be negative or above the valid group id range */
  }else{
    (void)sprintf(buf, "%d", (LONGSIZ32)groupID);
    (void)i1430_Error(ERR1430_NO_GROUP, buf, NULL);
    return (NULL);
  }
}

/******************************************************************************
 * Returns the logical address in <la> of the first module in the module
 * group <groupID>.
 * Returns 0 if OK, else negative error number;
 *****************************************************************************/
SHORTSIZ16 i1430_get_first_la(SHORTSIZ16 groupID, SHORTSIZ16 *la)
{
  aModGroupPtr ptr;

  if((ptr = i1430_valid_module_group(groupID)) == NULL) {/* not valid module group */
    return (ERR1430_NO_GROUP);
  }else{
    *la = e1430_modStates[*ptr].logicalAddr;
    return (0);
  }
}

/******************************************************************************
 * Return the number of modules in a group with ID = groupID.  Retuns 0 if
 * an invalid module group.
 *****************************************************************************/
SHORTSIZ16 i1430_number_of_modules(SHORTSIZ16 groupID)
{
  aModGroupPtr ptr;
  SHORTSIZ16 i;

  if((ptr = i1430_valid_module_group(groupID)) == NULL) { /* not valid module group */
    return 0;
  }else{
    for(i=0; *ptr != -1; i++) {
      ptr++;
    }
    return i;
  }
}


 /******************************************************************************
 * This function returns the index in e1430_modStates array of the module with the 
 * logical address, la.  Returns -1 if there is no module with this address 
 * in the array.
 *
 *****************************************************************************/
SHORTSIZ16 i1430_get_index_from_la(SHORTSIZ16 la, SHORTSIZ16 *index)
{
  int i;
  char buf[80];

  if(e1430_modStates == NULL || e1430_modStatesSize == 0) {
    goto bummer;
  }

  for(i=0; i < e1430_modStatesSize; i++) {
    if(la == e1430_modStates[i].logicalAddr) {
      *index = i;
      return (0);
    }
  }

bummer:
  (void)sprintf(buf, "%d", (LONGSIZ32)la);
  return (i1430_Error(ERR1430_LA_NOT_IN_MOD_GROUP, buf, NULL));
}

/******************************************************************************
 * This function initializes the module state structure to the defaults
 * that will occur inside a TRICE module after a reset.
 *****************************************************************************/
SHORTSIZ16 i1430_init_mod_state(SHORTSIZ16 la, aModuleImage DOS_FAR *statePtr)
{
  SHORTSIZ16 error, i, reg;

  /* determine slot and mainframe number of module */
  error = i1430_get_slot_and_cage(la, &(statePtr->slot), &(statePtr->cage));
  if(error) return (error);

  statePtr->logicalAddr = la;
  statePtr->analogSetup = 0;
  statePtr->offset = 0x800;
  statePtr->measControl = 0;
  statePtr->dataFormat = 0;
  statePtr->irqConfig0 = 0;
  statePtr->irqConfig1 = 0;
  statePtr->portControl = PORT_CONTROL_SEND_PORT_VME;
  statePtr->trigSetup = 0;
  statePtr->trigOffset = 0; 
  statePtr->trigBlocksize = 0; 
  statePtr->adcControl = 0;
  statePtr->trigPassout= 0;
  statePtr->timingSetup = 0;
  statePtr->zoomPhase0 = 0;
  statePtr->zoomPhase1 = 0;
  statePtr->zoomInterp0 = 0;
  statePtr->zoomInterp1 = 0;
  statePtr->zoomInterp2 = 0;
  statePtr->zoomInterp3 = 0;
  statePtr->zoomControl = 0;
  statePtr->trigLevelSave = 0xDD80;
  statePtr->trigSlopeSave = TRIGGER_SETUP_SLOPE_POS;
  statePtr->trigDelaySave = 0;

  for(i=0; i<RANGE_STEPS; i++) {
    statePtr->offsetSave[i] = 0x800;
  }

  error = e1430_get_status(la, &reg);
  if(error) return(error);

  if(reg & E1430_MEAS_STATUS_CLOCK_MASK) {
    statePtr->sampleFreq = (FLOATSIZ32)10240000.0;
  }else{
    statePtr->sampleFreq = (FLOATSIZ32)10000000.0;
  }

  return (0);
}

/******************************************************************************
 This function checks to see if a E1430 module is at the logical address, la,
 and, if so, creates and initializes a new state structure and expands the 
 e1430_modStates array to include it.  If it fails to find a E1430 module at the 
 logical address, or can't allocate memory for the new state structure, it will
 return -1, otherwise it returns the index into the e1430_modStates array at
 which the new state structure was added.
 *****************************************************************************/
static SHORTSIZ16 create_new_mod_state(SHORTSIZ16 la, SHORTSIZ16 *index)
{
  char buf[80];
  ULONGSIZ32 size, i;
  aModuleImage DOS_FAR *tempModStates;

  if(e1430_debug) {
    (void)printf("Looking for E1430A at logical address %d\n", la);
  }

  if(!i1430_trice_at_la(la)) {	/* no trice module at this logical address */
    (void)sprintf(buf, "%d", (LONGSIZ32)la);
    (void)i1430_Error(ERR1430_NO_MOD_AT_LA, buf, NULL);
    return (ERR1430_NO_MOD_AT_LA);
  }

  if(e1430_modStates == NULL){	/* first time; so no array yet */
    tempModStates = (aModuleImage DOS_FAR *)i1430_get_mem(sizeof(aModuleImage));
  }else{			/* expand existing array */
    size = (e1430_modStatesSize + 1) * sizeof(aModuleImage);
    tempModStates = (aModuleImage DOS_FAR *)i1430_get_mem(size); 
    if(tempModStates != NULL) {
      for(i=0; i< (ULONGSIZ32)e1430_modStatesSize; i++) {
        tempModStates[i] = e1430_modStates[i];
      }

      i1430_free_mem(e1430_modStates);
    }
  }

  if(tempModStates == NULL) {	/* couldn't realloc this memory */
    (void)i1430_Error(ERR1430_REALLOC, "e1430_modStates array", NULL);
    return (ERR1430_REALLOC);
  }

  e1430_modStates = tempModStates;  

  e1430_modStates[e1430_modStatesSize].logicalAddr = la;	/* last module is new one */

  *index = e1430_modStatesSize++;		

  return (0);
}

/******************************************************************************
 * creates a new module group from an array of logical addresses, logAddrArray,
 * whose size is arraySize.  It returns 0 if it is unable to allocate the
 * memory, or if no E1430 module exists at one of the logical addresses,
 * otherwise it returns a "id" number that is used for all subsequent 
 * references to this new module group.
 *****************************************************************************/
SHORTSIZ16 e1430_create_module_group(SHORTSIZ16 arraySize, 
					SHORTSIZ16 DOS_FAR *logAddrArray)
{
  SHORTSIZ16  tempID;		/* index into e1430_modGroupID array */
  aModHeadPtr tempHead; 	/* a pointer to the e1430_modGroupID array */
  aModGroupPtr tempGroup;	/* a pointer to a module group array */
  SHORTSIZ16 i, j;
  SHORTSIZ16 tempIndex;
  SHORTSIZ16 error;

  if(e1430_modGroupID == NULL) {		/* this is first call */
    e1430_modGroupID = (aModHeadPtr)i1430_get_mem((unsigned)(8 * sizeof(e1430_modGroupID)));
    if(e1430_modGroupID == NULL) {
      (void)i1430_Error(ERR1430_MALLOC, "e1430_modGroupID array", NULL);
      return (0);
    }

    for(i=0; i < 8; i++) {		/* set to all null pointers */
      e1430_modGroupID[i] = NULL;
    }
    e1430_modGroupIdSize = 8;
  }

  /* search for empty ID slot in array */
  for(tempID=0; tempID < e1430_modGroupIdSize; tempID++) {
    if(e1430_modGroupID[tempID] == NULL) break;
  }

  if(tempID == e1430_modGroupIdSize) { /* didn't find an empty slot, so expand */
    tempHead = (aModHeadPtr)i1430_get_mem((unsigned)((e1430_modGroupIdSize + 8) * 
							sizeof(aModGroupPtr)));
    if(tempHead == NULL) {
      (void)i1430_Error(ERR1430_REALLOC, "e1430_modGroupID array", NULL);
      return (0);
    }
  
    for(i=0; i<e1430_modGroupIdSize; i++) {
      tempHead[i] = e1430_modGroupID[i];
    }
    
    (void)i1430_free_mem(e1430_modGroupID);	/* free old array */
    e1430_modGroupID = tempHead;		/* new, expanded array */

    for(i=0; i < 8; i++) {		/* set to all null pointers */
      e1430_modGroupID[e1430_modGroupIdSize + i] = NULL;
    }
    e1430_modGroupIdSize += 8;
  }

  /* now at this point tempID should point to an empty slot in the 
   * e1430_modGroupID array, whether realloc'ed or not; so create a module
   * group */
  tempGroup = 
      (aModGroupPtr)i1430_get_mem((unsigned)((arraySize + 1) * sizeof(SHORTSIZ16))); 

  if(tempGroup == NULL) {    /* if unable to malloc memory */
    (void)i1430_Error(ERR1430_MALLOC, "module group", NULL);
    return (0);
  }

  tempGroup[arraySize] = -1;	/* terminate the array group */

  e1430_modGroupID[tempID] = tempGroup;

  /* now search e1430_modStates for an entry with each logical address in the
   * array logAddrArray and add a new one if it doesn't exist */
  for(i=0; i < arraySize; i++)	{ /* for every logical address in input array*/
    tempIndex = -1;
    for(j=0; j < e1430_modStatesSize; j++) {	/* for each element in e1430_modStates */
      if(logAddrArray[i] == e1430_modStates[j].logicalAddr) {
        tempIndex = j;
	break;
      }
    }
    if(tempIndex == -1)  {	/* not there, create new one */
      error = create_new_mod_state(logAddrArray[i], &tempIndex);
      if(error) {
	return(0);
      }else{			/* new module in group so reset it */
        /* DO THESE STEPS IN THIS ORDER */
	error = e1430_hard_reset(logAddrArray[i]);  /* hard reset of new mod */
	if(error) return(error);
        (void) i1430_init_mod_state(logAddrArray[i], &(e1430_modStates[tempIndex]));
        error = i1430_reset_E1430(logAddrArray[i]);	/* init new module */
        if(error) return 0;
      }
    }
    /* now put index of module data structure into this element of the
     * module group */
    tempGroup[i] = tempIndex;
  }
  
  /* all is well at this point; so return the index to the
   * e1430_modGroupID array + GROUPID_OFFSET, as the "handle" to refer 
   * to this new module group. GROUPID_OFFSET is to raise these id above
   * the range of logical addresses */

   if(tempID < 0) {
     return(0);
   }else{
     return (tempID + GROUPID_OFFSET);
   }

}

/******************************************************************************
 * This function removes a module group with the module group id, groupId. 
 * The entry for this id is removed from the e1430_modGroupID array and the
 * dynamic memory associated with the module group is freed.  It returns -1
 * if there is no module group associated with groupId, otherwise it returns 0.
 *****************************************************************************/
SHORTSIZ16 e1430_delete_module_group(SHORTSIZ16 groupID)
{
  char buf[80];
  aModGroupPtr ptr;

  ptr = i1430_valid_module_group(groupID);
  if(ptr == NULL) {
    (void)sprintf(buf, "%d", (LONGSIZ32)groupID);
    (void)i1430_Error(ERR1430_NO_GROUP, buf, NULL);
    return (ERR1430_NO_GROUP);
  }

  (void)i1430_free_mem((void *)e1430_modGroupID[groupID - GROUPID_OFFSET]);
  e1430_modGroupID[groupID - GROUPID_OFFSET] = NULL;

  return (0);
}

/******************************************************************************
 * This function frees all memory associated with module groups and the
 * module state structures.
 *****************************************************************************/
SHORTSIZ16 e1430_delete_all_module_groups(void)
{
  int i;

  for(i=0; i < e1430_modGroupIdSize; i++) {  /* free all module group arrays */
    if (e1430_modGroupID[i])
      (void)i1430_free_mem((void *)e1430_modGroupID[i]);
  }

  /* free e1430_modStates array */
  if (e1430_modStates)	
    (void)i1430_free_mem((void *)e1430_modStates);
  e1430_modStates = NULL;
  e1430_modStatesSize = 0;

  /* free e1430_modGroupID array */
  if (e1430_modGroupID)
    (void)i1430_free_mem((void *)e1430_modGroupID);
  e1430_modGroupID = NULL;
  e1430_modGroupIdSize = 0;

  return (0);
}
